package com.algo.rpc.client;

import com.algo.rpc.common.RpcRequest;
import com.algo.rpc.common.RpcResponse;
import com.alibaba.fastjson.JSON;
import org.apache.log4j.Logger;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * 创建远程调用对象工厂
 */
public class RemoteServiceProxy {

    private final static Logger logger = Logger.getLogger(RemoteServiceProxy.class);

    private static volatile RemoteServiceProxy INSTANCE;

    private static final String SERVICE_HOSTNAME = "127.0.0.1";

    private static final int SERVICE_PORT = 9999;

    private static final long DEFAULT_REQUEST_TIMEOUT = 5000;

    private RpcNettyClient client;

    private RemoteServiceProxy() {
        RpcClientConfig rpcClientConfig = new RpcClientConfig(SERVICE_HOSTNAME, SERVICE_PORT, DEFAULT_REQUEST_TIMEOUT);
        try {
            client = new RpcNettyClient();
            client.connect(rpcClientConfig);
        } catch (InterruptedException e) {
            logger.error("rpc client connect server interrupt");
        }
    }

    public static RemoteServiceProxy getInstance() {
        if (INSTANCE == null) {
            synchronized (RemoteServiceProxy.class) {
                if (INSTANCE == null) {
                    INSTANCE = new RemoteServiceProxy();
                }
            }
        }

        return INSTANCE;
    }

    /**
     * 创建实例方法
     * @param serviceInterface 接口类名
     * @param <T> 泛型类
     * @return 代理实例
     */
    public <T> T createInstance(Class<T> serviceInterface) {
        return (T) Proxy.newProxyInstance(RemoteServiceProxy.class.getClassLoader(),
                new Class[] {serviceInterface}, new RpcRemoteInvocationHandler(serviceInterface, client));
    }

    private static class RpcRemoteInvocationHandler implements InvocationHandler {

        private Class<?> serviceInterface;

        private RpcNettyClient client;

        public RpcRemoteInvocationHandler(Class<?> serviceInterface, RpcNettyClient client) {
            this.serviceInterface = serviceInterface;
            this.client = client;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            logger.info("invoke " + method.getName() + " with " + JSON.toJSONString(args));
            RpcRequest request = new RpcRequest();
            request.setRequestId(UUID.randomUUID().toString().replace("-", ""));
            request.setArgs(args);
            request.setParameterTypes(method.getParameterTypes());
            request.setMethodName(method.getName());
            request.setServiceInterfaceClass(serviceInterface);

            RpcResponse response = client.remoteCall(request);
            return response.getResult();
        }
    }


}
